all repos — caroster @ v0.8.0

[Octree] Group carpool to your event https://caroster.io

frontend/pages/e/[uuid].tsx (view raw)

  1import {useState, useReducer, useEffect} from 'react';
  2import Box from '@material-ui/core/Box';
  3import {makeStyles} from '@material-ui/core/styles';
  4import {useTranslation} from 'react-i18next';
  5import {initializeApollo} from '../../lib/apolloClient';
  6import useToastStore from '../../stores/useToastStore';
  7import useEventStore from '../../stores/useEventStore';
  8import Layout from '../../layouts/Default';
  9import AddToMyEventDialog from '../../containers/AddToMyEventDialog';
 10import TravelColumns from '../../containers/TravelColumns';
 11import NewTravelDialog from '../../containers/NewTravelDialog';
 12import VehicleChoiceDialog from '../../containers/VehicleChoiceDialog';
 13import WelcomeDialog from '../../containers/WelcomeDialog';
 14import EventBar from '../../containers/EventBar';
 15import Loading from '../../containers/Loading';
 16import OnBoardingTour from '../../containers/OnBoardingTour';
 17import {
 18  useUpdateEventMutation,
 19  Event as EventType,
 20  useEventByUuidQuery,
 21  EventByUuidDocument,
 22  EditEventInput,
 23  useFindUserVehiclesQuery,
 24} from '../../generated/graphql';
 25import ErrorPage from '../_error';
 26import useProfile from '../../hooks/useProfile';
 27import Fab from '../../containers/Fab';
 28
 29const POLL_INTERVAL = 10000;
 30
 31interface Props {
 32  event: EventType;
 33  eventUUID: string;
 34}
 35
 36const EventPage = props => {
 37  const {t} = useTranslation();
 38  const {event} = props;
 39  if (!event) return <ErrorPage statusCode={404} title={t`event.not_found`} />;
 40  return <Event {...props} />;
 41};
 42
 43const Event = (props: Props) => {
 44  const {eventUUID} = props;
 45  const classes = useStyles();
 46  const {t} = useTranslation();
 47  const {user} = useProfile();
 48  const {
 49    data: {me: {profile: {vehicles = []} = {}} = {}} = {},
 50    loading
 51  } = useFindUserVehiclesQuery();
 52  const addToast = useToastStore(s => s.addToast);
 53  const setEvent = useEventStore(s => s.setEvent);
 54  const eventUpdate = useEventStore(s => s.event);
 55  const setIsEditing = useEventStore(s => s.setIsEditing);
 56  const [updateEvent] = useUpdateEventMutation();
 57  const [isAddToMyEvent, setIsAddToMyEvent] = useState(false);
 58  const [openNewTravelContext, toggleNewTravel] = useState({opened: false});
 59  const [openVehicleChoice, toggleVehicleChoice] = useReducer(i => !i, false);
 60  const {data: {eventByUUID: event} = {}} = useEventByUuidQuery({
 61    pollInterval: POLL_INTERVAL,
 62    variables: {uuid: eventUUID},
 63  });
 64
 65  useEffect(() => {
 66    if (event) setEvent(event as EventType);
 67  }, [event]);
 68
 69  const onSave = async e => {
 70    try {
 71      const {uuid, ...data} = eventUpdate;
 72      const {id, __typename, travels, users, waitingList, ...input} = data;
 73      await updateEvent({
 74        variables: {uuid, eventUpdate: input as EditEventInput},
 75        refetchQueries: ['eventByUUID'],
 76      });
 77      setIsEditing(false);
 78    } catch (error) {
 79      console.error(error);
 80      addToast(t('event.errors.cant_update'));
 81    }
 82  };
 83
 84  const onShare = async () => {
 85    if (!event) return null;
 86    // If navigator share capability
 87    if (!!navigator.share)
 88      return await navigator.share({
 89        title: `Caroster ${event.name}`,
 90        url: `${window.location.href}`,
 91      });
 92    // Else copy URL in clipboard
 93    else if (!!navigator.clipboard) {
 94      await navigator.clipboard.writeText(window.location.href);
 95      addToast(t('event.actions.copied'));
 96      return true;
 97    }
 98  };
 99
100  const addTravelClickHandler =
101    user && vehicles?.length != 0
102      ? toggleVehicleChoice
103      : () => toggleNewTravel({opened: true});
104
105  if (!event || loading) return <Loading />;
106
107  return (
108    <Layout
109      pageTitle={t('event.title', {title: event.name})}
110      menuTitle={t('event.title', {title: event.name})}
111      displayMenu={false}
112    >
113      <EventBar
114        event={event}
115        onAdd={setIsAddToMyEvent}
116        onSave={onSave}
117        onShare={onShare}
118      />
119      <TravelColumns toggle={addTravelClickHandler} />
120      <Box className={classes.bottomRight}>
121        <Fab
122          onClick={addTravelClickHandler}
123          aria-label="add-car"
124          color="primary"
125        >
126          {t('travel.creation.title')}
127        </Fab>
128      </Box>
129      <NewTravelDialog
130        context={openNewTravelContext}
131        toggle={() => toggleNewTravel({opened: false})}
132      />
133      <VehicleChoiceDialog
134        open={openVehicleChoice}
135        toggle={toggleVehicleChoice}
136        toggleNewTravel={toggleNewTravel}
137        vehicles={vehicles}
138      />
139      <AddToMyEventDialog
140        event={event}
141        open={isAddToMyEvent}
142        onClose={() => setIsAddToMyEvent(false)}
143      />
144      <WelcomeDialog />
145      <OnBoardingTour />
146    </Layout>
147  );
148};
149
150export async function getServerSideProps(ctx) {
151  const {uuid} = ctx.query;
152  const apolloClient = initializeApollo();
153  const {data = {}} = await apolloClient.query({
154    query: EventByUuidDocument,
155    variables: {uuid},
156  });
157  const {eventByUUID: event} = data;
158  const {host = ''} = ctx.req.headers;
159
160  return {
161    props: {
162      event,
163      eventUUID: uuid,
164      metas: {
165        title: event?.name || '',
166        url: `https://${host}${ctx.resolvedUrl}`,
167      },
168    },
169  };
170}
171
172const useStyles = makeStyles(theme => ({
173  bottomRight: {
174    position: 'absolute',
175    bottom: theme.spacing(1),
176    right: theme.spacing(6),
177    width: 200,
178    [theme.breakpoints.down('sm')]: {
179      right: theme.spacing(1),
180    },
181  },
182}));
183
184export default EventPage;